// Clearone StreamNet Driver
// Copyright 2012 Control Concepts Inc.
// Programmer: JJM
// Date: 2/9/2012
// Version 1.0

System.Print("C1 StreamNet: Initializing Clearone StreamNet Driver\r\n");

////////////////////////////////////////////////////////////
// MAX STUFF
////////////////////////////////////////////////////////////
var MAX_RENDERERS = 128;
var MAX_SOURCES = 128;
var MAX_ZONES = 16;

////////////////////////////////////////////////////////////
// INIT STUFF
////////////////////////////////////////////////////////////
var INIT_NOT_STARTED = 0;
var INIT_GET_SOURCES = 1;
var INIT_GET_RENDERERS = 2;
var INIT_GET_RENDERER_STATUS = 3;
var INIT_COMPLETED = 4;

var initializedStatus = INIT_NOT_STARTED;

////////////////////////////////////////////////////////////
// GENERAL
////////////////////////////////////////////////////////////
var rendererCount = 0;
var sourceCount = 0;

////////////////////////////////////////////////////////////
// DRIVER PARAMETERS
////////////////////////////////////////////////////////////
var g_debug = Config.Get("DebugTrace") == "true";
var g_ZoneGroup = Config.Get("ZoneGroup");

////////////////////////////////////////////////////////////
// TCP CONNECTION SETUP
////////////////////////////////////////////////////////////
var g_comm = new TCP(OnCommRx, Config.Get("TCPAddress").toString(), parseInt(Config.Get("TCPPort"), 10));
g_comm.OnConnectFunc = OnConnect;
g_comm.OnDisconnectFunc = OnDisconnect;
g_comm.AddRxFraming("StopChar", "\x00");

////////////////////////////////////////////////////////////
// TIMERS
////////////////////////////////////////////////////////////
var g_hbtimer = new Timer();
var g_hbtimerInterval = 30000;
var g_hbtimerRunning = false;

var g_queue = new String();
var g_queuetimer = new Timer();
var g_queuetimerInterval = 1000;
var g_queuetimerRunning = false;

////////////////////////////////////////////////////////////
// DATA STORES FROM STREAMNET
////////////////////////////////////////////////////////////
var sourceInfoList = new Array();
var rendererInfoList = new Array();

////////////////////////////////////////////////////////////
// Intialize System Variables
////////////////////////////////////////////////////////////
var g_viewids = Config.Get("SYSTEM::TwoWayDeviceList");
var g_view_array = g_viewids.split(' ');
var g_list_array = new Array();

intializeViewSysVars();
intializeSysVars();

////////////////////////////////////////////////////////////
// Source Scrolling List
////////////////////////////////////////////////////////////
function sourceListScrollFuncton(viewID, highlight, top)
{
    if (g_debug == true)
        System.Print("C1 StreamNet: sourceScrollFunction view(" + viewID + ")  highlight(" + highlight + ") top(" + top + ")\r\n");

    for (var i = 0; i < g_view_array.length; i++)
    {
        if (g_view_array[i] == viewID)
        {
            var sourceNameList = g_list_array[i];

            sourceNameList.SetMarked(highlight);
        }
    }
}

////////////////////////////////////////////////////////////
// TCP
////////////////////////////////////////////////////////////
function OnConnect()
{
    if (g_debug == true)
        System.Print("C1 StreamNet: OnConnect()\r\n");

    initializeSystem(INIT_GET_SOURCES);

    StartHeartbeat();
}

function OnDisconnect()
{
    if (g_debug == true)
        System.Print("C1 StreamNet: OnDisconnect()\r\n");

    StopHeartbeat();
}

function OnCommRx(data)
{
    if (g_debug == true)
        System.Print("C1 StreamNet: OnCommRx data (" + data + ")\r\n");

    parseData(data);
}

function SendMessage(data)
{
    if (g_comm.ConnectState == true)
    {
        if (g_debug == true)
            System.Print("C1 StreamNet: SendMessage data (" + data + ")\r\n");

        var test = new String(data);

        if (test.indexOf("\x00") >= 0)
            g_comm.Write(data);
        else
            g_comm.Write(data + "\x00");
    }
}

function SendRendererMessage(zoneID, msg)
{
    if (g_comm.ConnectState == true)
    {
        if (g_debug == true)
            System.Print("C1 StreamNet: SendRendererMessage msg[" + zoneID + "] (" + msg + ")\r\n");

        var actualZone = getActualZone(zoneID);

        if (rendererInfoList.length >= actualZone)
        {
            var sendThis = new String();

            sendThis = sendThis.concat("#@", rendererInfoList[actualZone - 1].id, "#", msg);

            SendMessage(sendThis)
        }
    }
}

////////////////////////////////////////////////////////////
// Parsing Incoming Data
////////////////////////////////////////////////////////////
function parseData(msg)
{
    if (msg.indexOf("#REPORT") >= 0)
    {
        var reportReceiver = getReportReceiverName(msg);

        if (reportReceiver != "")
        {
            var zoneID = findZoneID(reportReceiver);

            if (zoneID > 0 && zoneID <= MAX_ZONES)
            {
                if (xmlAttributeExists(msg, "currentSource") == true)
                {
                    updateSelectedSource(zoneID, xmlAttributeParse(msg, "currentSource"));
                }
                else
                {
                    if (xmlAttributeExists(msg, "vol") == true)
                        updateVolume(zoneID, (new Number(xmlAttributeParse(msg, "vol"))).valueOf());

                    if (xmlAttributeExists(msg, "balance") == true)
                        updateBalance(zoneID, (new Number(xmlAttributeParse(msg, "balance"))).valueOf());

                    if (xmlAttributeExists(msg, "bass") == true)
                        updateBass(zoneID, (new Number(xmlAttributeParse(msg, "bass"))).valueOf());

                    if (xmlAttributeExists(msg, "treb") == true)
                        updateTreble(zoneID, (new Number(xmlAttributeParse(msg, "treb"))).valueOf());

                    if (xmlAttributeExists(msg, "band_1") == true)
                        updateEQBand(zoneID, 1, (new Number(xmlAttributeParse(msg, "band_1"))).valueOf());

                    if (xmlAttributeExists(msg, "band_2") == true)
                        updateEQBand(zoneID, 2, (new Number(xmlAttributeParse(msg, "band_2"))).valueOf());

                    if (xmlAttributeExists(msg, "band_3") == true)
                        updateEQBand(zoneID, 3, (new Number(xmlAttributeParse(msg, "band_3"))).valueOf());

                    if (xmlAttributeExists(msg, "band_4") == true)
                        updateEQBand(zoneID, 4, (new Number(xmlAttributeParse(msg, "band_4"))).valueOf());

                    if (xmlAttributeExists(msg, "band_5") == true)
                        updateEQBand(zoneID, 5, (new Number(xmlAttributeParse(msg, "band_5"))).valueOf());

                    if (xmlAttributeExists(msg, "mute") == true)
                        updateVolumeMute(zoneID, xmlAttributeParse(msg, "mute") == "1");

                    if (xmlAttributeExists(msg, "audioSessionActive") == true)
                        updateMultiRoomMode(zoneID, xmlAttributeParse(msg, "audioSessionActive") == "1");

                    if (xmlAttributeExists(msg, "audioSession") == true)
                        updateMultiRoomSessionName(zoneID, xmlAttributeParse(msg, "audioSession"));

                    if (xmlAttributeExists(msg, "ampOn") == true)
                        updateAVMode(zoneID, xmlAttributeParse(msg, "ampOn") == "1");
                }
            }
        }
    }
    else if (msg.indexOf("#MENU_RESP") >= 0)
    {
        if (msg.indexOf("idpath=\"sources\"") >= 0)
        {
            var itemNum = xmlAttributeParse(msg, "itemnum");

            if (itemNum != "" && itemNum != "-1")
            {
                var num = new Number(itemNum);

                if (num > 0 && num <= MAX_SOURCES)
                {
                    var index = num - 1;

                    sourceInfoList[index] = new SourceInfo();

                    sourceInfoList[index].id = xmlAttributeParse(msg, "id");
                    sourceInfoList[index].displayName = xmlAttributeParse(msg, "display");
                    sourceInfoList[index].type = xmlAttributeParse(msg, "type");
                    sourceInfoList[index].ipAddress = xmlAttributeParse(msg, "ip");

                    if (num > sourceCount)
                    {
                        sourceCount = num;

                        if (initializedStatus == INIT_GET_SOURCES && (num % 2) == 0)
                        {
                            if (queueHasItems() == true)
                            {
                                goodResponse();
                            }
                            else
                            {
                                stopQueueProcess();
                                initializeSystem(INIT_GET_RENDERERS);
                            }
                        }
                    }
                }
                else if (num > MAX_SOURCES)
                {
                    if (initializedStatus == INIT_GET_SOURCES)
                    {
                        stopQueueProcess();
                        initializeSystem(INIT_GET_RENDERERS);
                    }
                }
            }
            else if (itemNum == "-1")
            {
                if (initializedStatus == INIT_GET_SOURCES)
                {
                    stopQueueProcess();
                    initializeSystem(INIT_GET_RENDERERS);
                }
            }
        }
        else if (msg.indexOf("idpath=\"renderers\"") >= 0)
        {
            var itemNum = xmlAttributeParse(msg, "itemnum");

            if (itemNum != "" && itemNum != "-1")
            {
                var num = new Number(itemNum);

                if (num > 0 && num <= MAX_RENDERERS)
                {
                    var index = num - 1;

                    rendererInfoList[index] = new RendererInfo();
                    rendererInfoList[index].id = xmlAttributeParse(msg, "id");
                    rendererInfoList[index].displayName = xmlAttributeParse(msg, "display");
                    rendererInfoList[index].ipAddress = xmlAttributeParse(msg, "ip");
                    rendererInfoList[index].roomName = xmlAttributeParse(msg, "roomName");
                    rendererInfoList[index].permID = xmlAttributeParse(msg, "permId");

                    if (num > rendererCount)
                    {
                        rendererCount = num;

                        if (initializedStatus == INIT_GET_RENDERERS && (num % 2) == 0)
                        {
                            if (queueHasItems() == true)
                            {
                                goodResponse();
                            }
                            else
                            {
                                stopQueueProcess();
                                initializeSystem(INIT_GET_RENDERER_STATUS);
                            }
                        }
                    }
                }
                else if (num > MAX_RENDERERS)
                {
                    if (initializedStatus == INIT_GET_RENDERERS)
                    {
                        stopQueueProcess();
                        initializeSystem(INIT_GET_RENDERER_STATUS);
                    }
                }
            }
            else if (itemNum == "-1")
            {
                if (initializedStatus == INIT_GET_RENDERERS)
                {
                    stopQueueProcess();
                    initializeSystem(INIT_GET_RENDERER_STATUS);
                }
            }
        }
    }
}

////////////////////////////////////////////////////////////
// Parser Helpers
////////////////////////////////////////////////////////////
function xmlAttributeExists(msg, attribute)
{
    var findThis = " " + attribute + "=";

    return (msg.indexOf(findThis) >= 0);
}

function xmlAttributeParse(msg, attribute)
{
    var response = "";

    var findThis = " " + attribute + "=";

    var location = msg.indexOf(findThis);

    if (location >= 0)
    {
        var dataStart = msg.indexOf("\"", location);

        if (dataStart >= 0)
        {
            dataStart += 1;
            var dataEnd = msg.indexOf("\"", dataStart);

            if (dataEnd > dataStart)
            {
                response = msg.substring(dataStart, dataEnd);
            }
        }
    }

    return response;
}

function getReportReceiverName(msg)
{
    var response = "";

    var location = msg.indexOf(":");

    if (location >= 0)
    {
        location += 1;

        var endLocation = msg.indexOf("#REPORT", location);

        if (endLocation > location)
        {
            response = msg.substring(location, endLocation);
        }
    }

    return response;
}

////////////////////////////////////////////////////////////
// renderer info
////////////////////////////////////////////////////////////
function RendererInfo()
{
    this.id = "";
    this.displayName = "";
    this.ipAddress = "";
    this.roomName = "";
    this.permID;
}

////////////////////////////////////////////////////////////
// source info
////////////////////////////////////////////////////////////
function SourceInfo()
{
    this.id = "";
    this.displayName = "";
    this.ipAddress = "";
    this.type = "";
}

////////////////////////////////////////////////////////////
// initialize
////////////////////////////////////////////////////////////
function initializeSystem(state)
{
    switch (state)
    {
        case INIT_GET_SOURCES:

            for (var x = 1; x <= MAX_SOURCES; x += 2)
            {
                enQueue("#MENU_LIST " + x + "," + (x + 1) + ",sources");
            }

            startQueueProcess();

            initializedStatus = state;

            break;

        case INIT_GET_RENDERERS:

            for (var x = 1; x <= MAX_RENDERERS; x += 2)
            {
                enQueue("#MENU_LIST " + x + "," + (x + 1) + ",renderers");
            }

            startQueueProcess();

            initializedStatus = state;

            break;

        case INIT_GET_RENDERER_STATUS:

            var startZone = ((g_ZoneGroup * MAX_ZONES) + 1);
            var endZone = ((g_ZoneGroup * MAX_ZONES) + MAX_ZONES);

            for (var zone = startZone; zone <= endZone; zone++)
            {
                var index = zone - 1;

                if (rendererInfoList.length >= zone)
                {
                    SendMessage("#@" + rendererInfoList[index].id + "#QUERY RENDERER");
                    SendMessage("#@" + rendererInfoList[index].id + "#QUERY CURRENT_SOURCE");
                    SendMessage("#REGISTER ON,{{" + rendererInfoList[index].id + "}}");

                    var zoneID = ((index % MAX_ZONES) + 1);

                    updateRendererName(zoneID, rendererInfoList[index].id)
                    updateIsRegistered(zoneID, true);
                }
                else
                {
                    break;
                }
            }

            initializedStatus = state;

            initializeSystem(INIT_COMPLETED);

            break;

        case INIT_COMPLETED:

            buildSourceList();

            initializedStatus = state;

            break;
    }
}

function intializeSysVars()
{
    var sysvar = new String();

    for (var zoneID = 1; zoneID <= MAX_ZONES; zoneID++)
    {
        SystemVars.Write(sysvar.concat("Volume", "Zone", zoneID2String(zoneID), "Sysvar"), 0);
        SystemVars.Write(sysvar.concat("VolumeMute", "Zone", zoneID2String(zoneID), "Sysvar"), false);
        SystemVars.Write(sysvar.concat("Bass", "Zone", zoneID2String(zoneID), "Sysvar"), 0);
        SystemVars.Write(sysvar.concat("Treble", "Zone", zoneID2String(zoneID), "Sysvar"), 0);
        SystemVars.Write(sysvar.concat("Balance", "Zone", zoneID2String(zoneID), "Sysvar"), 0);
        SystemVars.Write(sysvar.concat("EQBand1", "Zone", zoneID2String(zoneID), "Sysvar"), 0);
        SystemVars.Write(sysvar.concat("EQBand2", "Zone", zoneID2String(zoneID), "Sysvar"), 0);
        SystemVars.Write(sysvar.concat("EQBand3", "Zone", zoneID2String(zoneID), "Sysvar"), 0);
        SystemVars.Write(sysvar.concat("EQBand4", "Zone", zoneID2String(zoneID), "Sysvar"), 0);
        SystemVars.Write(sysvar.concat("EQBand5", "Zone", zoneID2String(zoneID), "Sysvar"), 0);
        SystemVars.Write(sysvar.concat("AVMode", "Zone", zoneID2String(zoneID), "Sysvar"), false);
        SystemVars.Write(sysvar.concat("MultiRoom", "Zone", zoneID2String(zoneID), "Sysvar"), false);
        SystemVars.Write(sysvar.concat("MultiSessionName", "Zone", zoneID2String(zoneID), "Sysvar"), "");
        SystemVars.Write(sysvar.concat("SelectedSource", "Zone", zoneID2String(zoneID), "Sysvar"), "");
        SystemVars.Write(sysvar.concat("IsRegistered", "Zone", zoneID2String(zoneID), "Sysvar"), false);
        SystemVars.Write(sysvar.concat("RendererName", "Zone", zoneID2String(zoneID), "Sysvar"), "");
    }
}

function intializeViewSysVars()
{
    // Creating Source List & Selected Zone values for each view
    for (var i = 0; i < g_view_array.length; i++)
    {
        var list = new SystemVarsList("SourceListSysvar%" + g_view_array[i]);
        list.OnScrollInfoFunc = sourceListScrollFuncton;

        g_list_array.push(list);

        var sysvar = new String();

        SystemVars.Write(sysvar.concat("ZoneID", "SelectedZoneSysvar%", g_view_array[i]), 0);
        SystemVars.Write(sysvar.concat("Volume", "SelectedZoneSysvar%", g_view_array[i]), 0);
        SystemVars.Write(sysvar.concat("VolumeMute", "SelectedZoneSysvar%", g_view_array[i]), false);
        SystemVars.Write(sysvar.concat("Bass", "SelectedZoneSysvar%", g_view_array[i]), 0);
        SystemVars.Write(sysvar.concat("Treble", "SelectedZoneSysvar%", g_view_array[i]), 0);
        SystemVars.Write(sysvar.concat("Balance", "SelectedZoneSysvar%", g_view_array[i]), 0);
        SystemVars.Write(sysvar.concat("EQBand1", "SelectedZoneSysvar%", g_view_array[i]), 0);
        SystemVars.Write(sysvar.concat("EQBand2", "SelectedZoneSysvar%", g_view_array[i]), 0);
        SystemVars.Write(sysvar.concat("EQBand3", "SelectedZoneSysvar%", g_view_array[i]), 0);
        SystemVars.Write(sysvar.concat("EQBand4", "SelectedZoneSysvar%", g_view_array[i]), 0);
        SystemVars.Write(sysvar.concat("EQBand5", "SelectedZoneSysvar%", g_view_array[i]), 0);
        SystemVars.Write(sysvar.concat("AVMode", "SelectedZoneSysvar%", g_view_array[i]), false);
        SystemVars.Write(sysvar.concat("MultiRoom", "SelectedZoneSysvar%", g_view_array[i]), false);
        SystemVars.Write(sysvar.concat("MultiSessionName", "SelectedZoneSysvar%", g_view_array[i]), "");
        SystemVars.Write(sysvar.concat("SelectedSource", "SelectedZoneSysvar%", g_view_array[i]), "");
        SystemVars.Write(sysvar.concat("IsRegistered", "SelectedZoneSysvar%", g_view_array[i]), false);
        SystemVars.Write(sysvar.concat("RendererName", "SelectedZoneSysvar%", g_view_array[i]), "");
    }
}

function buildSourceList()
{
    for (var i = 0; i < g_view_array.length; i++)
    {
        var list = g_list_array[i];

        list.Open();

        for (var sourceItem = 0; sourceItem < sourceInfoList.length; sourceItem++)
        {
            list.Insert(sourceInfoList[sourceItem].displayName);
        }

        list.Close();
    }
}

////////////////////////////////////////////////////////////
// My Queue - Need more control than the comm queue offers
////////////////////////////////////////////////////////////
function startQueueProcess()
{
    if (g_queuetimerRunning == false && queueHasItems() == true)
    {
        g_queuetimerRunning = true;

        SendMessage(deQueue());

        g_queuetimer.Start(OnQueueTimeout, g_queuetimerInterval);
    }
}

function stopQueueProcess()
{
    clearQueue();

    g_queuetimer.Stop();

    g_queuetimerRunning = false;
}

function OnQueueTimeout()
{
    g_queuetimerRunning = false;

    startQueueProcess();
}

function goodResponse()
{
    g_queuetimer.Stop();

    g_queuetimerRunning = false;

    startQueueProcess();
}

function enQueue(msg)
{
    var test = new String(msg);

    if (test.indexOf("\x00") >= 0)
        g_queue = g_queue.concat(msg);
    else
        g_queue = g_queue.concat(msg, "\x00");
}

function deQueue()
{
    var location = g_queue.indexOf("\x00");

    if (location >= 0)
    {
        var msg = g_queue.slice(0, location);

        g_queue = g_queue.substring(location + 1, g_queue.length);

        return msg;
    }

    return "";
}

function clearQueue()
{
    g_queue = "";
}

function queueHasItems()
{
    return (g_queue.length > 0);
}

////////////////////////////////////////////////////////////
// HeartBeat Timer
////////////////////////////////////////////////////////////
function OnHeartbeat()
{
    g_hbtimerRunning = false;

    SendMessage("#HEARTBEAT");

    var startZone = ((g_ZoneGroup * MAX_ZONES) + 1);
    var endZone = ((g_ZoneGroup * MAX_ZONES) + MAX_ZONES);

    for (var zone = startZone; zone <= endZone; zone++)
    {
        var index = zone - 1;

        if (rendererInfoList.length >= zone)
            SendMessage("#REGISTER ON,{{" + rendererInfoList[index].id + "}}");
        else
            break;
    }

    StartHeartbeat();
}

function StartHeartbeat()
{
    if (g_hbtimerRunning == false)
    {
        g_hbtimerRunning = true;

        g_hbtimer.Start(OnHeartbeat, g_hbtimerInterval);
    }
}

function StopHeartbeat()
{
    if (g_hbtimerRunning == true)
    {
        g_hbtimerRunning = false;

        g_hbtimer.Stop();
    }
}

////////////////////////////////////////////////////////////
// ZoneID Helpers
////////////////////////////////////////////////////////////
function zoneID2String(zoneID)
{
    var zoneIDString = "";

    if (zoneID <= 9)
        zoneIDString = "0" + zoneID;
    else
        zoneIDString = "" + zoneID;

    return zoneIDString;
}

function getActualZone(zoneID)
{
    return ((g_ZoneGroup * MAX_ZONES) + zoneID);
}

function getZoneID(actualZone)
{
    return (((actualZone - 1) % MAX_ZONES) + 1);
}

function findZoneID(rendererID)
{
    var response = 0;

    var startZone = ((g_ZoneGroup * MAX_ZONES) + 1);
    var endZone = ((g_ZoneGroup * MAX_ZONES) + MAX_ZONES);

    for (var zone = startZone; zone <= endZone; zone++)
    {
        var index = zone - 1;
        if (rendererInfoList.length >= zone)
        {
            if (rendererInfoList[index].id == rendererID)
            {
                response = (index % MAX_ZONES) + 1;
                break;
            }
        }
        else
            break;
    }

    return response;
}

////////////////////////////////////////////////////////////
// view helpers
////////////////////////////////////////////////////////////
function updateSelectedZoneValue(variable, zoneID, value)
{
    var sysvar = new String();

    for (var i = 0; i < g_view_array.length; i++)
    {
        var selID = SystemVars.Read(sysvar.concat("ZoneID", "SelectedZoneSysvar%", g_view_array[i]));

        if (zoneID == selID)
        {
            SystemVars.Write(sysvar.concat(variable,  "SelectedZoneSysvar%", g_view_array[i]), value);
        }
    }
}

function viewZoneID(zoneID, viewID)
{
    var sysvar = new String();

    if (viewID != "-1" && viewID != "" && zoneID == 0)
    {
        var selID = SystemVars.Read(sysvar.concat("ZoneID", "SelectedZoneSysvar%", viewID));

        return (selID > 0) ? selID : -1;
    }
    else if (zoneID > 0)
    {
        return zoneID;
    }

    return -1;
}

////////////////////////////////////////////////////////////
// Update System Variables
////////////////////////////////////////////////////////////
function updateVolume(zoneID, value)
{
    var sysvar = new String();

    sysvar = sysvar.concat("Volume", "Zone", zoneID2String(zoneID), "Sysvar");

    SystemVars.Write(sysvar, value);

    updateSelectedZoneValue("Volume", zoneID, value);
}

function updateVolumeMute(zoneID, state)
{
    var sysvar = new String();

    sysvar = sysvar.concat("VolumeMute", "Zone", zoneID2String(zoneID), "Sysvar");

    SystemVars.Write(sysvar, state);

    updateSelectedZoneValue("VolumeMute", zoneID, state);
}

function updateBalance(zoneID, value)
{
    var sysvar = new String();

    sysvar = sysvar.concat("Balance", "Zone", zoneID2String(zoneID), "Sysvar");

    SystemVars.Write(sysvar, value);

    updateSelectedZoneValue("Balance", zoneID, value);
}

function updateBass(zoneID, value)
{
    var sysvar = new String();

    sysvar = sysvar.concat("Bass", "Zone", zoneID2String(zoneID), "Sysvar");

    SystemVars.Write(sysvar, value);

    updateSelectedZoneValue("Bass", zoneID, value);
}

function updateTreble(zoneID, value)
{
    var sysvar = new String();

    sysvar = sysvar.concat("Treble", "Zone", zoneID2String(zoneID), "Sysvar");

    SystemVars.Write(sysvar, value);

    updateSelectedZoneValue("Treble", zoneID, value);
}

function updateEQBand(zoneID, band, value)
{
    var sysvar = new String();

    sysvar = sysvar.concat("EQBand" + band, "Zone", zoneID2String(zoneID), "Sysvar");

    SystemVars.Write(sysvar, value);

    updateSelectedZoneValue("EQBand" + band, zoneID, value);
}

function updateAVMode(zoneID, state)
{
    var sysvar = new String();

    sysvar = sysvar.concat("AVMode", "Zone", zoneID2String(zoneID), "Sysvar");

    SystemVars.Write(sysvar, state);

    updateSelectedZoneValue("AVMode", zoneID, state);
}

function updateMultiRoomMode(zoneID, state)
{
    var sysvar = new String();

    sysvar = sysvar.concat("MultiRoom", "Zone", zoneID2String(zoneID), "Sysvar");

    SystemVars.Write(sysvar, state);

    updateSelectedZoneValue("MultiRoom", zoneID, state);
}

function updateMultiRoomSessionName(zoneID, name)
{
    var sysvar = new String();

    sysvar = sysvar.concat("MultiRoomSessionName", "Zone", zoneID2String(zoneID), "Sysvar");

    SystemVars.Write(sysvar, name);

    updateSelectedZoneValue("MultiRoomSessionName", zoneID, name);
}

function updateSelectedSource(zoneID, source)
{
    var sysvar = new String();

    sysvar = sysvar.concat("SelectedSource", "Zone", zoneID2String(zoneID), "Sysvar");

    SystemVars.Write(sysvar, source);

    updateSelectedZoneValue("SelectedSource", zoneID, source);
}

function updateIsRegistered(zoneID, state)
{
    var sysvar = new String();

    sysvar = sysvar.concat("IsRegistered", "Zone", zoneID2String(zoneID), "Sysvar");

    SystemVars.Write(sysvar, state);

    updateSelectedZoneValue("IsRegistered", zoneID, state);
}

function updateRendererName(zoneID, name)
{
    var sysvar = new String();

    sysvar = sysvar.concat("RendererName", "Zone", zoneID2String(zoneID), "Sysvar");

    SystemVars.Write(sysvar, name);

    updateSelectedZoneValue("RendererName", zoneID, name);
}

////////////////////////////////////////////////////////////
// Validation Helpers
////////////////////////////////////////////////////////////
function validateNum(value)
{
    return (value >= 0 && value <= 100);
}

function validateNum(value, min, max)
{
    return (value >= min && value <= max);
}

////////////////////////////////////////////////////////////
// ZoneSelection
////////////////////////////////////////////////////////////
function SelectZoneID(zoneID, viewID)
{
    if (viewID == "-1" || viewID == "")
        return;

    var sysvar = new String();

    SystemVars.Write(sysvar.concat("ZoneID", "SelectedZoneSysvar%", viewID), zoneID);

    // gets zones values....
    var volume = SystemVars.Read(sysvar.concat("Volume", "Zone", zoneID2String(zoneID), "Sysvar"));
    var volumeMute = SystemVars.Read(sysvar.concat("VolumeMute", "Zone", zoneID2String(zoneID), "Sysvar"));
    var bass = SystemVars.Read(sysvar.concat("Bass", "Zone", zoneID2String(zoneID), "Sysvar"));
    var treble = SystemVars.Read(sysvar.concat("Treble", "Zone", zoneID2String(zoneID), "Sysvar"));
    var balance = SystemVars.Read(sysvar.concat("Balance", "Zone", zoneID2String(zoneID), "Sysvar"));
    var EQBand1 = SystemVars.Read(sysvar.concat("EQBand1", "Zone", zoneID2String(zoneID), "Sysvar"));
    var EQBand2 = SystemVars.Read(sysvar.concat("EQBand2", "Zone", zoneID2String(zoneID), "Sysvar"));
    var EQBand3 = SystemVars.Read(sysvar.concat("EQBand3", "Zone", zoneID2String(zoneID), "Sysvar"));
    var EQBand4 = SystemVars.Read(sysvar.concat("EQBand4", "Zone", zoneID2String(zoneID), "Sysvar"));
    var EQBand5 = SystemVars.Read(sysvar.concat("EQBand5", "Zone", zoneID2String(zoneID), "Sysvar"));
    var avMode = SystemVars.Read(sysvar.concat("AVMode", "Zone", zoneID2String(zoneID), "Sysvar"));
    var mr = SystemVars.Read(sysvar.concat("MultiRoom", "Zone", zoneID2String(zoneID), "Sysvar"));
    var mrSName = SystemVars.Read(sysvar.concat("MultiSessionName", "Zone", zoneID2String(zoneID), "Sysvar"));
    var ss = SystemVars.Read(sysvar.concat("SelectedSource", "Zone", zoneID2String(zoneID), "Sysvar"));
    var isReg = SystemVars.Read(sysvar.concat("IsRegistered", "Zone", zoneID2String(zoneID), "Sysvar"));
    var RenName = SystemVars.Read(sysvar.concat("RendererName", "Zone", zoneID2String(zoneID), "Sysvar"));

    // assign them to this views Select Zone Values.
    SystemVars.Write(sysvar.concat("Volume", "SelectedZoneSysvar%", viewID), volume);
    SystemVars.Write(sysvar.concat("VolumeMute", "SelectedZoneSysvar%", viewID), volumeMute);
    SystemVars.Write(sysvar.concat("Bass", "SelectedZoneSysvar%", viewID), bass);
    SystemVars.Write(sysvar.concat("Treble", "SelectedZoneSysvar%", viewID), treble);
    SystemVars.Write(sysvar.concat("Balance", "SelectedZoneSysvar%", viewID), balance);
    SystemVars.Write(sysvar.concat("EQBand1", "SelectedZoneSysvar%", viewID), EQBand1);
    SystemVars.Write(sysvar.concat("EQBand2", "SelectedZoneSysvar%", viewID), EQBand2);
    SystemVars.Write(sysvar.concat("EQBand3", "SelectedZoneSysvar%", viewID), EQBand3);
    SystemVars.Write(sysvar.concat("EQBand4", "SelectedZoneSysvar%", viewID), EQBand4);
    SystemVars.Write(sysvar.concat("EQBand5", "SelectedZoneSysvar%", viewID), EQBand5);
    SystemVars.Write(sysvar.concat("AVMode", "SelectedZoneSysvar%", viewID), avMode);
    SystemVars.Write(sysvar.concat("MultiRoom", "SelectedZoneSysvar%", viewID), mr);
    SystemVars.Write(sysvar.concat("MultiSessionName", "SelectedZoneSysvar%", viewID), mrSName);
    SystemVars.Write(sysvar.concat("SelectedSource", "SelectedZoneSysvar%", viewID), ss);
    SystemVars.Write(sysvar.concat("IsRegistered", "SelectedZoneSysvar%", viewID), isReg);
    SystemVars.Write(sysvar.concat("RendererName", "SelectedZoneSysvar%", viewID), RenName);
}

////////////////////////////////////////////////////////////
// Volume
////////////////////////////////////////////////////////////
function RaiseVolume(zoneID, viewID)
{
    var id = viewZoneID(zoneID, viewID);

    if (id == -1)
        return;

    SendRendererMessage(id, "LEVEL_UP VOL");
}

function LowerVolume(zoneID, viewID)
{
    var id = viewZoneID(zoneID, viewID);

    if (id == -1)
        return;

    SendRendererMessage(id, "LEVEL_DN VOL");
}

function SetVolume(zoneID, value, viewID)
{
    var id = viewZoneID(zoneID, viewID);

    if (id == -1)
        return;

    if (validateNum(value) == false)
        return;

    SendRendererMessage(id, "LEVEL_SET VOL," + value);
}

function SetVolumeMute(zoneID, mute, viewID)
{
    var id = viewZoneID(zoneID, viewID);

    if (id == -1)
        return;

    var sState = mute ? "ON" : "OFF";

    SendRendererMessage(id, "MUTE " + sState);
}


function ToggleVolumeMute(zoneID, viewID)
{
    var id = viewZoneID(zoneID, viewID);

    if (id == -1)
        return;

    SendRendererMessage(id, "MUTE TOGGLE");
}

////////////////////////////////////////////////////////////
// Balance
////////////////////////////////////////////////////////////
function SetBalance(zoneID, value, viewID)
{
    var id = viewZoneID(zoneID, viewID);

    if (id == -1)
        return;

    if (validateNum(value) == false)
        return;

    SendRendererMessage(id, "LEVEL_SET BALANCE," + value);
}

function AdjustBalanceLeft(zoneID, viewID)
{
    var id = viewZoneID(zoneID, viewID);

    if (id == -1)
        return;

    SendRendererMessage(id, "LEVEL_UP BALANCE");
}

function AdjustBalanceRight(zoneID, viewID)
{
    var id = viewZoneID(zoneID, viewID);

    if (id == -1)
        return;

    SendRendererMessage(id, "LEVEL_DN BALANCE");
}

function CenterBalance(zoneID, viewID)
{
    var id = viewZoneID(zoneID, viewID);

    if (id == -1)
        return;

    SendRendererMessage(id, "LEVEL_SET BALANCE,50");
}

////////////////////////////////////////////////////////////
// Bass
////////////////////////////////////////////////////////////
function RaiseBass(zoneID, viewID)
{
    var id = viewZoneID(zoneID, viewID);

    if (id == -1)
        return;

    SendRendererMessage(id, "LEVEL_UP BASS");
}

function LowerBass(zoneID, viewID)
{
    var id = viewZoneID(zoneID, viewID);

    if (id == -1)
        return;

    SendRendererMessage(id, "LEVEL_DN BASS");
}

function SetBass(zoneID, value, viewID)
{
    var id = viewZoneID(zoneID, viewID);

    if (id == -1)
        return;

    if (validateNum(value) == false)
        return;

    SendRendererMessage(id, "LEVEL_SET BASS," + value);
}

////////////////////////////////////////////////////////////
// Treble
////////////////////////////////////////////////////////////
function RaiseTreble(zoneID, viewID)
{
    var id = viewZoneID(zoneID, viewID);

    if (id == -1)
        return;

    SendRendererMessage(id, "LEVEL_UP TREB");
}

function LowerTreble(zoneID, viewID)
{
    var id = viewZoneID(zoneID, viewID);

    if (id == -1)
        return;

    SendRendererMessage(id, "LEVEL_DN TREB");
}

function SetTreble(zoneID, value, viewID)
{
    var id = viewZoneID(zoneID, viewID);

    if (id == -1)
        return;

    if (validateNum(value) == false)
        return;

    SendRendererMessage(zoneID, "LEVEL_SET TREB," + value);
}

////////////////////////////////////////////////////////////
// EQBands
////////////////////////////////////////////////////////////
function RaiseEQBand(zoneID, band, viewID)
{
    var id = viewZoneID(zoneID, viewID);

    if (id == -1)
        return;

    SendRendererMessage(id, "LEVEL_UP BAND_" + band);
}

function LowerEQBand(zoneID, band, viewID)
{
    var id = viewZoneID(zoneID, viewID);

    if (id == -1)
        return;

    SendRendererMessage(zoneID, "LEVEL_DN BAND_" + band);
}

function CenterEQBand(zoneID, band, viewID)
{
    var id = viewZoneID(zoneID, viewID);

    if (id == -1)
        return;

    SendRendererMessage(id, "LEVEL_SET BAND_" + band + ",50");
}

function SetEQBand(zoneID, band, value, viewID)
{
    var id = viewZoneID(zoneID, viewID);

    if (id == -1)
        return;

    if (validateNum(value) == false)
        return;

    SendRendererMessage(id, "LEVEL_SET BAND_" + band + "," + value);
}

////////////////////////////////////////////////////////////
// AVMode
////////////////////////////////////////////////////////////
function ToggleAVMode(zoneID, viewID)
{
    var id = viewZoneID(zoneID, viewID);

    if (id == -1)
        return;

    var sysvar = new String();

    var state = SystemVars.Read(sysvar.concat("AVMode", "Zone", zoneID2String(id), "Sysvar"));

    SetAVMode(id, !state);
}

function SetAVMode(zoneID, state, viewID)
{
    var id = viewZoneID(zoneID, viewID);

    if (id == -1)
        return;

    var sState = state ? "ON" : "OFF";

    SendRendererMessage(id, "ACTIVE " + sState);
}

////////////////////////////////////////////////////////////
// MultiRoom
////////////////////////////////////////////////////////////
function ToggleMultiRoom(zoneID, viewID)
{
    var id = viewZoneID(zoneID, viewID);

    if (id == -1)
        return;

    var sysvar = new String();

    var state = SystemVars.Read(sysvar.concat("MultiRoom", "Zone", zoneID2String(id), "Sysvar"));

    SetMultiMode(id, !state);
}

function SetMultiMode(zoneID, state, viewID)
{
    var id = viewZoneID(zoneID, viewID);

    if (id == -1)
        return;

    var sysvar = new String();

    var sessionName = SystemVars.Read(sysvar.concat("MultiRoomSessionName", "Zone", zoneID2String(id), "Sysvar"));

    if (sessionName == null || sessionName == "")
    {
        sessionName = "All Rooms MultiRoom";
        SetMultiRoomSessionName(id, sessionName);
    }

    if (state)
        SendRendererMessage(id, "MULTIAUDIO JOIN {{" + sessionName + "}}");
    else
        SendRendererMessage(id, "MULTIAUDIO LEAVE");

}

function SetMultiRoomSessionName(zoneID, name, viewID)
{
    var id = viewZoneID(zoneID, viewID);

    if (id == -1)
        return;

    var sysvar = new String();

    var sessionName = SystemVars.Write(sysvar.concat("MultiRoomSessionName", "Zone", zoneID2String(id), "Sysvar"), name);
}

////////////////////////////////////////////////////////////
// Source Selection
////////////////////////////////////////////////////////////
function SetSource(zoneID, viewID)
{
    var id = viewZoneID(zoneID, viewID);

    if (id == -1)
        return;

    for (var i = 0; i < g_view_array.length; i++)
    {
        if (g_view_array[i] == viewID)
        {
            var sourceNameList = g_list_array[i];
            
            if (sourceNameList.MarkedCount > 0)
            {
                var foundOne = false;

                for (var markedIndex = 0; markedIndex < sourceNameList.MarkedCount; markedIndex++)
                {
                    // I am only going to accept the first marked item in the list.
                    if (foundOne == false)
                    {
                        foundOne = true;
                        SendRendererMessage(id, "SRC_SEL {{" + sourceNameList.ReadAt(sourceNameList.GetMarked(markedIndex)) + "}}");
                    }

                    // I am going to remove all marked ones, as an indication that the method worked.
                    sourceNameList.Open();
                    sourceNameList.RemoveMarked(sourceNameList.GetMarked(markedIndex));
                    sourceNameList.Close();
                }
            }

            break;
        }
    }
}
